#include <string.h>
#include "lz77.h"

#define DEFAULT_DICTIONARYLEN	255	//2^8 - 1 car il faut coder 0
#define DEFAULT_READBUFLEN		255	// ~2^16 comparaisons dans le pire des cas
#define DEFAULT_BEGCODESIZE		8 //Nombre de bits pour coder i
#define DEFAULT_LENCODESIZE		8 //Nombre de bits pour coder j


typedef struct{
	bloc_t	beg;	//i
	bloc_t	len;	//j
	byte	c;		//k
} lz77code_t;


uint dicLen = DEFAULT_DICTIONARYLEN;	//current length of dictionary
uint rbufLen= DEFAULT_READBUFLEN;

uint begCodeSize= DEFAULT_BEGCODESIZE;
uint lenCodeSize= DEFAULT_LENCODESIZE;



//-----------------------------------------------------------------------------
void LZ77_init(uint dictionaryLen, uint readBufLen)
//-----------------------------------------------------------------------------
{
	bloc_t i,log=1;

	dicLen	= dictionaryLen;
	rbufLen	= readBufLen;

	for(i=dicLen; i>1; i/=2){log++;}
	begCodeSize = log;

	log=1;
	for(i=rbufLen; i>1; i/=2){log++;}
	lenCodeSize = log;
	printf("Initialisation du codage sur (%d,%d) bits\n",begCodeSize,lenCodeSize);
}


//-----------------------------------------------------------------------------
lz77code_t LZ77_getCode(bitbuf_t * buf)
//-----------------------------------------------------------------------------
//Recherche brutale dans le dictionaire du plus long facteur de substitution
//uniquement si'il reste des données a consommer
{
	lz77code_t code;
	byte * win=buf->curs-dicLen; //pointeur du début du dictionnaire
	uint localdiclen ; //longueur du dictionnaire "local"
	uint localreadbuflen; // longueur de la fenetre de lecture "locale"

	byte * winEnd=buf->curs+rbufLen;	//pointeur vers la fin de fenetre de lecture
	byte * buffEnd = buf->data+buf->len;  //pointeur vers la fin du buffer

	if (win < buf->data){localdiclen=buf->curs-buf->data;} //initialisation de la taille du dico local
	else {localdiclen=dicLen;}


	if (winEnd>buffEnd)
	{localreadbuflen=buffEnd-buf->curs;} //initialisation de la taille de la fenêtre de lecture locale
	else {localreadbuflen=rbufLen;}

	byte * beg=win;			//debut du facteur
	byte * p=beg;			//pointeur de recherche dans le dictionnaire
							//(pour trouver le plus long facteur debutant à beg)
	byte * rbuf= buf->curs;	//pointeur de recherche dans le tampon de lecture



	uint maxfactLen=0;		//taille du plus long facteur trouvé
	uint factLen=0;			//taille du facteur courrant
	byte * fact = beg;

int i;
	//printf("Dico (%d) : ",dicLen);
		for(i=-localdiclen; i< 0; i++) printf("%c", buf->curs[i]);
		printf("|");
		for(i=0; i< localreadbuflen; i++) printf("%c",buf->curs[i]);
		printf(" => ");


		while( beg < buf->curs ) //pour tout le dictionnaire
		{
			rbuf=buf->curs;
			factLen =0;
			while((p < win+dicLen) && (rbuf < winEnd) && (*rbuf == *p) )
			//tq  non fin dico ET non fin tampon de lecture ET car identiques
			{
				rbuf++; p++; factLen++;
			}
			if( factLen > maxfactLen )
			{
				maxfactLen = factLen;
				fact = beg;
				if( maxfactLen == rbufLen )	{ printf("BREAK !!!!");break;}
			}

			beg++;
			p=beg;
		}



		if((!localdiclen)|(maxfactLen == 0)) //retour code spécial
		{
			code.beg = 0;
			code.len = 1;
			code.c	  = *rbuf;

		}else{	//retour code normal
			code.beg = (buf->curs - fact);
			code.len = maxfactLen;
			//code.c	 = ... inutile
		}

		if(code.beg)	printf("(%d,%d) ",code.beg, code.len);
		else			printf("%c ",code.c);


		buf->curs+=code.len; //décalage de la fenêtre de la taille de ce que l'on a ajouté dans le dico

		return code;

}


//-----------------------------------------------------------------------------
bitcount_t LZ77_writeCode(lz77code_t * code, bitbuf_t * dst)
//-----------------------------------------------------------------------------
//Écrire le code dans le buffer => retourne le nombre de bits ecrit
{
	bitcount_t wrote=begCodeSize;

	bit_write(dst, code->beg, begCodeSize);
	//printf("\noffset curs = %d val= 0x%02X\n", dst->curs - dst->data, dst->curs[0]);
	if(code->beg == 0){
		bit_write(dst, code->c, 8);
		wrote += 8;
	}else{
		//TODO optimiser le nombre de bits pour len en fonction de beg
		bit_write(dst, code->len, lenCodeSize);
		wrote += lenCodeSize;
	}

	//printf("\toffset curs = %d val= 0x%02X\n", dst->curs - dst->data, dst->curs[0]);
	return wrote;
}


//-----------------------------------------------------------------------------
bitcount_t LZ77_readCode(lz77code_t * code, bitbuf_t * dst)
//-----------------------------------------------------------------------------
//Lire un code depuis le buffer => retroune le nombre de bits lus
{
	bitcount_t read= begCodeSize;

	code->beg = bit_read(dst, begCodeSize);
	if(code->beg == 0){
		code->len = 0;
		code->c   = bit_read(dst, 8);
		read += 8;
	}else{
		code->len = bit_read(dst, lenCodeSize);
		read += lenCodeSize;
	}

	return read;
}


//-----------------------------------------------------------------------------
void LZ77_encode(bitbuf_t * dst, bitbuf_t * src)
//-----------------------------------------------------------------------------
{
	size_t i;
	lz77code_t	code;

	for(i=0; i<src->len;)
	{
		code=LZ77_getCode(src/*->curs+i)*/);

		printf("%d bits\n",LZ77_writeCode(&code, dst));
		i+=(!code.len)?8:code.len*8;
	}
	dst->len =  bit_getpos(dst); /* dst->curs - dst->data;*/
}


//-----------------------------------------------------------------------------
void LZ77_decode(bitbuf_t * dst, bitbuf_t * src)
//-----------------------------------------------------------------------------
{
	size_t i,tmp;
	lz77code_t	code;

	for(i=0; i<(src->len);)
	{
		tmp = LZ77_readCode(&code, src);
		printf("readCode (%dbits)=> ",tmp);
		i+= tmp;
		/*if(dst->len < (dst->curs - dst->data) + 1 + code.len/8)
		{
			printf("Réallocation mémoire :( berk ! ");
			tmp = (dst->curs - dst->data);
			printf()
			realloc(dst->data,dst->len+(dst->len/2)+1); //+1/5
			dst->curs = dst->data + tmp;
		}
		*/

		if(code.beg != 0){
			printf("(%d,%d)\n",code.beg, code.len);
			//substitution
			memcpy(dst->curs, (dst->curs - code.beg), code.len);
			dst->curs += code.len;
		}else{
			printf("%c\n",code.c);
			//insertion d'un nouveau caractère
			*dst->curs = code.c;
			dst->curs++;
		}
	}

}

//-----------------------------------------------------------------------------
void LZ77_compress(FILE * out, FILE * in)
//-----------------------------------------------------------------------------
//premier rush on fixe les buffers a 8ko et on gaspille des bits dans le fichier
//TODO ne plus faire de gaspillage de bits
{
	bitbuf_t src,dst;
	int bufLen = 8192;	//8ko
	int len=0,i=1;
	double progress=0.;
	double sTauxComp=0.;
	size_t fileLen;
	lz78header header={LZ78ID,bufLen,0};

	//calcul de la taille du fichier
	fseek(in,0,SEEK_END);
	fileLen = ftell(in);
	rewind(in);

	printf("filLen= %ld\n",fileLen);
	header.oriSize = fileLen;
	fwrite(&header, sizeof(lz78header),1,out);

	//initialisation des buffers
	bit_initBuf(&src,bufLen*8);
	bit_initBuf(&dst,bufLen*8);

	//initialisation de l'algo
	LZ78_init();
	updateBar(0.);

	while(!feof(in))
	{
		len=fread(src.data, 1, bufLen, in);
		printf("\nlen=%d i= %d progress= %f sTauxComp = %f\n",len,i, progress, sTauxComp);
		updateBar(progress/fileLen);printf(" (%02d%%)\n",(int)(sTauxComp/(double)i * 100.));
		src.len=8*len;	//1octet = 8bits
		dst.len=bufLen;
		bit_seek(&src,0,0,BITSEEK_SET);
		bit_seek(&dst,0,0,BITSEEK_SET);

		LZ78_encode(&dst,&src);
		//printf("\n"); bit_print(&dst);printf("\n || ");
		//for(i=0; i<(dst.len)/8 +1; i++) printf("0x%02X ",dst.data[i]);
		//printf(" ||\n");
		fwrite(&dst.len,2, 1, out);
		fwrite(dst.data, ((dst.len)/8)+1,1,out);

		printf("\n src.len = %ld bits dst.len = %ld bits (0x%02lX)\n",src.len, dst.len, dst.len);
		sTauxComp += (double)dst.len/(double)src.len ;
		progress+=(double)len;
		i++; //compteur de boucle
	}
	printf("\nlen = %d progress=%f\n",len,progress);
	updateBar(progress/fileLen); printf(" (%02d%%)",(int)(sTauxComp/(double)i * 100.));
	printf("\n :D\n");

	bit_freeBuf(&src);
	bit_freeBuf(&dst);
}

//-----------------------------------------------------------------------------
void LZ77_decompress(FILE * out, FILE * in)
//-----------------------------------------------------------------------------
{
	bitbuf_t src,dst;
	int bufLen=0;
	double progress=0.,cpt=0.;
	lz78header header;

	fread(&header, sizeof(lz78header),1,in);
	bufLen=header.bufLen;
	printf("bufLen =%dbytes oriSize=%ldbytes\n",bufLen, header.oriSize);

	//initialisation des buffers
	bit_initBuf(&src,bufLen*108);
	bit_initBuf(&dst,bufLen*108);

	//initialisation de l'algo
	LZ78_init();
	//updateBar(0.);

	src.len=0;
	fread(&(src.len), 2,1,in);
	while( ! feof(in) )
	{
		dst.len=bufLen*8;

		printf("src.len = %ld (0x%02lX)\n",src.len,src.len);
		fread(src.data, 1, (src.len/8)+1, in);

		updateBar(progress/(header.oriSize*8));

		bit_seek(&src,0,0,BITSEEK_SET);
		bit_seek(&dst,0,0,BITSEEK_SET);

		LZ78_decode(&dst, &src);

		//printf("\nOHOHOHOHOH !!!!\n");

		//printf("%d - dst.len = %ld\n",__LINE__,dst.len);
		fwrite(dst.data, dst.len/8, 1, out);	//dst.len = 8*k
		cpt += src.len;
		progress += dst.len;
		src.len=0;
		fread(&(src.len), 2,1,in);
	}
	updateBar(progress/(header.oriSize*8));

	printf("\n :D\n");
	bit_freeBuf(&src);
	bit_freeBuf(&dst);
}







/*
//-----------------------------------------------------------------------------
int main()
//-----------------------------------------------------------------------------
{
	bitbuf_t dst, src, dst2;
	char msg[]="aabbaaaaaaaaabbaabbbaaaabba";//ababbaabaaaaaabbbbabbbaaaabbaa";
	int len = 26;
	int i;

	printf("%s (%d)\n",msg,len);

	LZ77_init(8,8);

	bit_initBuf(&dst, len*8);
	bit_initBuf(&src,len*8);
	bit_initBuf(&dst2,len*8);

//	src = LZ77_createEncodeBuffer(len);
	for(i=0; i<len; i++) {src.data[i] = msg[i];}




	LZ77_encode(&dst, &src);
	bit_print(&dst);
	printf("lz77_encode => %d\n",bit_getpos(&dst));

	bit_seek(&dst,0,0,BITSEEK_SET);
	printf("seekset => %d\n",bit_getpos(&dst));

//	dst2 = LZ77_createDecodeBuffer(len);

	bzero(dst2.data,len);
	LZ77_decode(&dst2,&dst);
	printf("decode -> \"%s\"\n",dst2.data);


	return EXIT_SUCCESS;
}
*/
//

